import datetime
import json
import os.path
import time

from fastapi import  FastAPI , Request
#from fastapi_offline import  FastAPIOffline
from utils.znUtils import  TPyDB , TMq , TPyInfluxDB
from svrTimer import  TTimer
from calculator import TCalculator



class TZnServer:
    def __init__(self):
        self.settings ={}
        self.mqClient = TMq()
        self.db = TPyDB()
        self.tdb = TPyInfluxDB()
        self.ters ={}
        self.buses={}
        self.devices={}
        self.vars={}
        self.tasks={}
        self.varValues ={}
        self.svrTimer = TTimer()
        self.calcultor = TCalculator()
    # objects
    def setSettings(self , settings):
        print('server load objects')
        try:
            self.settings = settings
            self.calcultor.tdb = self.tdb
            self.calcultor.tdbCode = self.settings['tdb']['code']
        except Exception as er:
            print(er)
    # objects
    def load(self):
        print('server load objects')
        try:
            dbSettings = self.settings['db']
            self.db.setSettings(**dbSettings)
            rs = self.db.queryByKey(dbSettings['code'],'serverObjs',{})
            ters = rs["datasetList"][0]
            buses = rs["datasetList"][1]
            devices = rs["datasetList"][2]
            vars = rs["datasetList"][3]
            self.ters={}
            for t in ters:
                terCode = t["terCode"]
                self.ters[terCode] = t
            self.buses={}
            for b in buses:
                busCode = b['busCode']
                self.buses[busCode] = b
            self.devices = {}
            for d in devices:
                devCode = d['devCode']
                self.devices[devCode] = d
            self.vars = {}
            self.tasks={}
            for v in vars:
                varKey = v['varKey']
                self.varValues[varKey] ={
                    "k":varKey,
                    "id":v['varId'],
                    "t":0 ,
                    "v":0 ,
                    "v0":0 ,
                    "d":"",
                    'regs':[]
                }
                terCode = v['terCode']
                busCode = v['busCode']
                regStart = v['regStart']
                regLen = v['regLen']
                duration = v['duration']
                unitId = v['unitId']
                taskKey = busCode +'_'+str(regStart)
                self.vars[varKey] = v
                if taskKey not in self.tasks.keys():
                    self.tasks[taskKey] ={
                        "terCode": terCode,
                        "busCode": busCode,
                        "taskKey": taskKey ,
                        "regStart": regStart,
                        "regLen":0,
                        'duration':duration,
                        'unitId':unitId,
                        "varKeys":[]
                    }
                self.tasks[taskKey]['varKeys'].append(v['varKey'])
                if regLen> self.tasks[taskKey]['regLen']:
                    self.tasks[taskKey]['regLen'] = regLen

            tdbSettings = self.settings['tdb']
            self.tdb.setSettings(**tdbSettings)
            print('server objects is loaded')

        except Exception as er:
            print(er)
    def getTerData(self , terCode):
        status = 0
        res={
            "terInfo":{},
            "buses":{},
            "tasks":{},
            'devices':{},
            "vars":{}
        }
        try:
            if terCode in self.ters.keys():
                res['terInfo'] = self.ters[terCode]
                for b in self.buses.values():
                    if b['terCode'] == terCode:
                        res['buses'][b['busCode']] = b
                for t in self.tasks.values():
                    if t['terCode'] == terCode:
                        res['tasks'][t['taskKey']] = t
                for d in self.devices.values():
                    if d['terCode'] == terCode:
                        res['devices'][d['devCode']] = d
                for v in self.vars.values():
                    if v['terCode'] == terCode:
                        res['vars'][v['varKey']] = v
                status = 1
            else:
                status = -1000
        except Exception as er:
            print(er)
        return  status , res
    def reload(self):
        try:
            pass
        except Exception as er:
            print(er)

    def startTimer(self):
        try:
            self.svrTimer.start()
        except Exception as er:
            print(er)

    #  mqtt client
    def startMqtt(self):
        res = False
        try:
            if self.mqClient == None:
                self.mqClient = TMq()
            mqSettings = self.settings['mq']
            self.mqClient.setSettings(**mqSettings)
            self.mqClient.restart(True)
        except Exception as er:
            print(er)
        return  res
    def stopMqtt(self):
        try:
            if self.mqClient!=None:
                self.mqClient.stop()
        except Exception as er:
            print(er)

    # update variables
    def updateVars(self, vars):
        try:
            notifyMsg = {}
            tdbDatas = []
            flag =False
            for v in vars:
                try:
                    varKey = v['k']
                    self.varValues[varKey] = v
                    notifyMsg[varKey] = v['v']
                    tdbItem = self.getVarTDBData(varKey , v['v'] , v['t'])
                    tdbDatas.append(tdbItem)
                except Exception as err:
                    print(err)
            mqConnected = self.mqClient.isConnected()
            if mqConnected :
                msg = json.dumps(notifyMsg)
                topic = "mq/zniot2024/var"
                self.mqClient.sendMsg(topic , msg)

            postData = "\n".join(tdbDatas)
            if flag :
                print('------------------   wx power ------------')
                print(postData)
                print('------------------   wx power ------------')
            self.tdb.write2TDB( self.settings['tdb']['code'] , postData, '')
        except Exception as er:
            print(er)

    def getVarTDBData(self , varKey , v  , t):
        res =""
        try:
            tabname = "variots"
            varObj = self.vars[varKey]
            tags= {
                'vid':varObj['varId']
            }
            fields = [  x+'='+str(tags[x]) for x in tags.keys()]
            tm = str(int(1000*1000*1000*t))
            pv = int(v)
            decLen = varObj['decLen']
            if decLen==1 :
                pv = 0.1*int(v*10)
            elif decLen==2 :
                pv = 0.01*int(v*100)
            elif decLen==3 :
                pv = 0.001*int(v*1000)
            elif decLen==4 :
                pv = 0.0001*int(v*10000)
            res = tabname+","+",".join(fields)+' v='+str(v)+' '+tm
        except Exception as er:
            print(er)
        return  res

    '''
    2025-01-05之前的数据结构
    '''
    def getVarTDBData_old(self , varKey , v  , t):
        res =""
        try:
            tabname = "varZniot2024"
            varObj = self.vars[varKey]
            tags= {
                "buseCode": varObj['busCode'] ,
                'unitId':varObj['unitId'],
                'devCode':varObj['devCode'],
                'varId':varObj['varId'],
                'mon':datetime.datetime.now().strftime("%Y%m")
            }
            fields = [  x+'='+str(tags[x]) for x in tags.keys()]
            tm = str(int(1000*1000*1000*t))
            res = tabname+","+",".join(fields)+' v='+str(v)+' '+tm
        except Exception as er:
            print(er)
        return  res

    def getArchitecture(self ):
        res ={
            "terminals":[],
            "devices":[],
            "variables":[]
        }
        try:
            res['terminals'] = [{
                "terCode": x['terCode'] ,
                "terName": x["terDesc"] ,
                "apiUrl" : x["apiUrl"]
            } for x in self.ters.values()]
            res['devices'] = [{
                "terCode": x['terCode'] ,
                "devCode": x["devCode"] ,
                "devName" : x["devName"]
            } for x in self.devices.values()]
            res['variables'] = [{
                "terCode": x['terCode'] ,
                "devCode": x["devCode"] ,
                "varId" : x["varId"] ,
                "varKey" : x["varKey"] ,
                "varDesc" : x["varDesc"] ,
                "varUnit" : x["varUnit"] ,
                "varOrder" : x["varOrder"],
                "varType" : x["varType"],
                "funType" : x["funType"]
            } for x in self.vars.values()]
        except Exception as er:
            print(er)
        return  res
    def getVarValues(self , devCode):
        res ={}
        try:
            if devCode == None or devCode=='':
                res = self.varValues
            else :
                vars = [self.varValues[v['varKey']] for v in self.vars.values() if v['devCode']==devCode]
                for v in vars:
                    res[v['k']] = v
        except Exception as er:
            print(er)
        return  res
    def getRealValues(self , varId , startTm , endTm):
        res ={
            "status":0,
            "result": None
        }
        try:
            varKey = str(varId)
            if varKey.find('$') <0 :
                varKey='var$'+str(varId)
            if varKey in self.vars.keys():
                varId = self.vars[varKey]["varId"]
                #sql = "select * from varZniot2024 where varId='{varId}' and time  >='{startTm}'  and time  <='{endTm}'    order by time limit 10000 tz('Asia/Shanghai')"
                sql = "select * from variots where vid='{varId}' and time  >='{startTm}'  and time  <='{endTm}'    order by time limit 10000 tz('Asia/Shanghai')"

                ps={
                    "varId": varId ,
                    "startTm":startTm ,
                    "endTm": endTm
                }
                #postSql = sql.replace(f"{varId}" , str(varId)).replace("{startTm}", startTm).replace("{endTm}", endTm)
                postSql = sql.format(**ps)
                queryRes = self.tdb.query(self.settings['tdb']['code'] , postSql )
                if queryRes['status'] == 1 :
                    columns = queryRes['result'][0]['series'][0]['columns']
                    values = queryRes['result'][0]['series'][0]['values']
                    resData = [dict(zip(columns , x)) for x in values]
                    #resData = dict(zip(columns , values))
                    res['result'] = resData
                    res['status'] = 1
            else:
                res["status"] = 10002
                res['msg'] = "varId("+str(varId)+") is error."
        except Exception as er:
            print(er)
        return  res
    def getVarReals(self, varIds):
        res=[]
        try:
            varIdKeys = varIds.split(',')
            for varId in varIdKeys:
                info = {}
                k = 'var$'+str(varId)
                if k in self.varValues.keys():
                    info = self.varValues[k]
                res.append({
                    "varId": k ,
                    "varInfo":info
                })
            '''
            varIdList = ["varId='"+str(x)+"'" for x in  list(varIds.split(","))]
            varCond = " or ".join(varIdList)
            sql ="select v from varZniot2024 where ("+varCond+") group by varId order by time desc limit 1  tz('Asia/Shanghai')"
            queryRes = self.tdb.query(self.settings['tdb']['code'] , sql )
            res = queryRes
            '''
        except Exception as er:
            print(er)
        return  res


    def exe1677(self):
        sql ="select v from varZniot2024 where varId='1677'"
        queryRes = self.tdb.query(self.settings['tdb']['code'] , sql )

        vs = queryRes['result'][0]['series'][0]['values']
        ts = [t[0] for t in vs]


        '''
        c = len(ts)
        sqls=[]
        size=10
        l = c // size + 1
        ind = 0
        for i in range(l):
            start = i*l
            end = start + l
            cond = " or ".join( ["time='"+x+"'" for x in ts[start:end]])
            s = "delete from varZniot2024 where "+cond
            exeRes = self.tdb.query(self.settings['tdb']['code'], s)
            ind = ind+1
            print('delete:', ind , l)
        '''

        index =0
        for t in ts:
            vsql = "delete from varZniot2024 where time='"+ t +"'"
            exeRes = self.tdb.query(self.settings['tdb']['code'] , vsql )
            index = index + 1
            print('delete point :', index , len(ts))

        print('over')
    def downloadDatas(self):
        tm = ''
        tmFn = "D:/project/znmutilprotocol/znIotSys/appdatas\his/tm.txt"
        with open(tmFn , 'r' , encoding='utf-8') as f:
            tm = f.read()

        #tm = '2024-11-19T04:10:43.287785984Z'
        tdbCode = self.settings['tdb']['code']
        dataPath = "D:/project/znmutilprotocol/znIotSys/appdatas/his"
        size = 1000000
        sqlMax = "select count(*) from varZniot2024 where time>'"+tm+"'"
        sqlFormat = "select * from varZniot2024 where time>'"+tm+"' order by time  limit {pageSize}  offset {pos}"

        maxRes = self.tdb.query(tdbCode, sqlMax)
        totalRows = maxRes['result'][0]['series'][0]['values'][0][-2]
        print('total rows:' , totalRows)

        pos =0
        while pos< totalRows:
            try:
                ps={
                    "pageSize": size ,
                    "pos":pos
                }
                sql = sqlFormat.format(**ps)
                time.sleep(3)
                t0 = time.time()
                dataRes = self.tdb.query(tdbCode , sql)
                values = dataRes['result'][0]['series'][0]['values']
                datas = [[v[0] , v[-3] , v[-1] if v[-1]!=None else v[-2] ]for v in values]
                fn = datas[-1][0]
                fn = fn.replace('-','_')
                fn = fn.replace('-','_')
                fn = fn.replace('T','_')
                fn = fn.replace(':','_')
                fn = fn.replace(':','_')
                fn = fn.replace('.','_')
                fn = fn + '.txt'
                fn = os.path.dirname(__file__)+"/appdatas/his/"+fn
                content = json.dumps(datas)
                with open(fn , 'w' , encoding='utf-8') as f:
                    f.write(content)
                with open(tmFn , 'w' , encoding='utf-8') as f:
                    f.write(datas[-1][0])
                t1 = time.time()
                print(pos , '/', totalRows , len(datas) , t1 - t0 )
                datas = None
                content = None
                values = None
                dataRes=None
                time.sleep(1)
            except Exception as er:
                print(er)
            pos = pos + size
        print('success')
    def convertData(self):
        dataPath = 'D:/project/znmutilprotocol/znIotSys/appdatas/his'
        fns = [ x for x in  os.listdir(dataPath) if x[0:3]=='202']
        dictPoints={}
        for fn in fns:
            fullFn = dataPath+"/"+fn
            if os.path.exists(fullFn):
                pts=[]
                with open(fullFn , 'r' , encoding='utf-8')as f :
                    c = f.read()
                    pts = json.loads(c)
                    c = None
                for pt in pts:
                    tm = pt[0]
                    v = pt[1]
                    varId = pt[2]
                    if varId!=None and len(varId)>0:
                        k = varId+"_"+tm
                        dictPoints[k] = v
                print(fn , len(pts))
                pts = None
        dataFn = dataPath+"/points.txt"
        with open(dataFn,'w',encoding='utf-8') as f:
            json.dump(dictPoints,f)
        print('success')
    def writeData(self):
        dataPath = 'D:/project/znmutilprotocol/znIotSys/appdatas/datas'
        tdbCode = self.settings['tdb']['code']
        fns = [ x for x in  os.listdir(dataPath) if x[0:3]=='pt_']
        for fn in fns:
            fullFn = dataPath+"/"+fn
            if os.path.exists(fullFn):
                c=""
                with open(fullFn , 'r' , encoding='utf-8')as f :
                    c = f.read()
                    #c = c.replace("'",'')
                res = self.tdb.write2TDB(tdbCode, c)
                print(fn , res)
                if res!=1 :
                    print('-------  error ---' , fn)

                c = None
        print('success')

    def postPoints(self):
        dataPath = 'D:/project/znmutilprotocol/znIotSys/appdatas/his/points.txt'
        dictPoints ={}
        with open( dataPath,'r', encoding='utf-8') as f:
            dictPoints = json.load(f)
        keys = list(dictPoints.keys())
        pos = 0
        total = len(keys)
        batch = 1


        pagesize= 200000
        while pos<=total:
            start = pos
            end = pos+ pagesize
            def getTm(tStr):
                t0 = int(time.mktime(time.strptime(tStr[:19],'%Y-%m-%dT%H:%M:%S'))) + 8*3600
                t1 =  tStr[20:-1]+'000000000'
                t1 = t1[0:9]
                t = str(t0) + t1

                return str(t)



            #pts= [  [x.split('_')[1] , x.split('_')[0] , dictPoints[x]]  for x in keys[start: end]]
            content = "\n".join([ "variots,vid="+x.split("_")[0]+' v='+str(dictPoints[x])+" "+ getTm(x.split("_")[1]) for x in keys[start: end]])
            bn = ('0000'+str(batch))[-4:]
            fn = 'D:/project/znmutilprotocol/znIotSys/appdatas/datas/pt_'+bn+".txt"
            with open(fn , 'w' , encoding='utf-8') as f :
                f.write(content)
            content = None
            pos = pos + pagesize
            batch = batch + 1
            print(batch,'ok')
        print('success',len(keys))

    def writeData20250101(self):
        datas = [
            'variots,vid=1113 v=421729.2 1735660801000000000',
            'variots,vid=1114 v=428317.2 1735660801000000000',
            'variots,vid=1115 v=407803.2 1735660801000000000',
            'variots,vid=1116 v=839344.8 1735660801000000000',
            'variots,vid=1118 v=12516570 1735660801000000000',
            'variots,vid=1119 v=777720 1735660801000000000',
            'variots,vid=1677 v=14204 1735660801000000000'
        ]
        c="\n".join(datas)
        tdbCode = self.settings['tdb']['code']
        res = self.tdb.write2TDB(tdbCode, c)
        print('success')



app = FastAPI()
#app = FastAPIOffline()
znServer = TZnServer()
print('create server app.')
@app.get("/")
def appRoot():
    return {
        "msg":"zn iot server api."
    }
@app.get("/zn/reload")
def appReload():
    res ={
        "action":"reload"
    }
    try:
        znServer.load()
    except Exception as er:
        print(er)
    return res
@app.get("/zn/getTerData")
def appGetTerData(terCode:str):
    res ={
        "status":0 ,
        "data":{}
    }
    try:
        status , data = znServer.getTerData(terCode)
        res['status'] = status
        res['data'] = data
    except Exception as er:
        print(er)
    return res
@app.post("/zn/varPost")
async def appPostVar( req:Request):
    res ={
        "status":0 ,
        "data":{}
    }
    try:
        data = await req.json()
        vars = data['vars']
        znServer.updateVars(vars)
        res['status'] = 1
    except Exception as er:
        print(er)
    return res
@app.get("/zn/getArchitecture")
def appGetArchitecture():
    res = znServer.getArchitecture()
    return res
@app.get("/zn/getRealValues")
def appGetRealValues(devCode:str = None):
    res = znServer.getVarValues(devCode)
    return res
@app.get("/zn/getVarHis")
def appGetRealValues(varId:str , startTm :str, endTm:str):
    res = znServer.getRealValues(varId , startTm.replace(" 08:00",'+08:00') , endTm.replace(" 08:00","+08:00"))
    return res
@app.get("/zn/getLiyangPowersBJ")
def getLiyangPowersBJ( y:int, m:int , d:int , h:int  ):
    res = znServer.calcultor.getLiyangPowersBJ(y,m,d,h)
    return res


@app.post("/items")
async def read_item(item: dict):
    return {"item": item}
@app.post("/zn/calculate")
async def znCalculate( params:dict ):
    res = params
    return res

@app.post("/zntdb/query")
async  def znTdbQuery(ps:dict):
    res = {
        "status":-1 ,
        "result":None
    }
    try:
        params={
            "tdbCode" : ps['tdbCode'] ,
            "dbKey" : ps['dbKey'] ,
            "queryPs" : ps['queryPs'] ,
        }
        res = znServer.tdb.queryByFn(**params)
    except Exception as er:
        print(er)
    return res
@app.post("/db/query")
async def znDbQuery(ps:dict):
    res = {
        "status":-1 ,
        "result":None
    }
    try:
        dbCode = ps['dbCode']
        dbKey = ps['dbKey']
        dbPs = ps['dbPs']
        res = znServer.db.queryByKey(dbCode , dbKey , dbPs)
    except Exception as er:
        print(er)
    return res
@app.get("/zn/getReals")
async def znGetVarReals(varIds : str):
    res = {
        "status":-1 ,
        "result":None
    }
    try:
        res = znServer.getVarReals(varIds)
    except Exception as er:
        print(er)
    return res